001 /* 002 * Copyright 2003-2005 The Apache Software Foundation 003 * Copyright 2005 Stephen McConnell 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package net.dpml.cli.option; 018 019 import java.util.ArrayList; 020 import java.util.Collections; 021 import java.util.Comparator; 022 import java.util.HashSet; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.ListIterator; 026 import java.util.Set; 027 028 import net.dpml.cli.Argument; 029 import net.dpml.cli.DisplaySetting; 030 import net.dpml.cli.Group; 031 import net.dpml.cli.OptionException; 032 import net.dpml.cli.WriteableCommandLine; 033 import net.dpml.cli.resource.ResourceConstants; 034 import net.dpml.cli.resource.ResourceHelper; 035 036 /** 037 * A Parent implementation representing normal switch options. 038 * For example: <code>+d|-d</code> or <code>--enable-x|--disable-x</code>. 039 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 040 * @version 1.0.0 041 */ 042 public class Switch extends ParentImpl 043 { 044 /** i18n */ 045 public static final ResourceHelper RESOURCES = 046 ResourceHelper.getResourceHelper(); 047 048 /** 049 * The default prefix for enabled switches 050 */ 051 public static final String DEFAULT_ENABLED_PREFIX = "+"; 052 053 /** 054 * The default prefix for disabled switches 055 */ 056 public static final String DEFAULT_DISABLED_PREFIX = "-"; 057 058 private final String m_enabledPrefix; 059 private final String m_disabledPrefix; 060 private final Set m_triggers; 061 private final String m_preferredName; 062 private final Set m_aliases; 063 private final Set m_prefixes; 064 private final Boolean m_defaultSwitch; 065 066 /** 067 * Creates a new Switch with the specified parameters 068 * @param enabledPrefix the prefix used for enabled switches 069 * @param disabledPrefix the prefix used for disabled switches 070 * @param preferredName the preferred name of the switch 071 * @param aliases the aliases by which the Switch is known 072 * @param description a description of the Switch 073 * @param required whether the Option is strictly required 074 * @param argument the Argument belonging to this Parent, or null 075 * @param children the Group children belonging to this Parent, ot null 076 * @param id the unique identifier for this Option 077 * @param switchDefault the switch default value 078 * @throws IllegalArgumentException if the preferredName or an alias isn't 079 * prefixed with enabledPrefix or disabledPrefix 080 */ 081 public Switch( 082 final String enabledPrefix, final String disabledPrefix, final String preferredName, 083 final Set aliases, final String description, final boolean required, 084 final Argument argument, final Group children, final int id, 085 final Boolean switchDefault ) 086 throws IllegalArgumentException 087 { 088 super( argument, children, description, id, required ); 089 090 if( enabledPrefix == null ) 091 { 092 throw new IllegalArgumentException( 093 RESOURCES.getMessage( 094 ResourceConstants.SWITCH_NO_ENABLED_PREFIX ) ); 095 } 096 097 if( disabledPrefix == null ) 098 { 099 throw new IllegalArgumentException( 100 RESOURCES.getMessage( 101 ResourceConstants.SWITCH_NO_DISABLED_PREFIX ) ); 102 } 103 104 if( enabledPrefix.startsWith( disabledPrefix ) ) 105 { 106 throw new IllegalArgumentException( 107 RESOURCES.getMessage( 108 ResourceConstants.SWITCH_ENABLED_STARTS_WITH_DISABLED ) ); 109 } 110 111 if( disabledPrefix.startsWith( enabledPrefix ) ) 112 { 113 throw new IllegalArgumentException( 114 RESOURCES.getMessage( 115 ResourceConstants.SWITCH_DISABLED_STARTWS_WITH_ENABLED ) ); 116 } 117 118 m_enabledPrefix = enabledPrefix; 119 m_disabledPrefix = disabledPrefix; 120 m_preferredName = preferredName; 121 122 if( ( preferredName == null ) || ( preferredName.length() < 1 ) ) 123 { 124 throw new IllegalArgumentException( 125 RESOURCES.getMessage( 126 ResourceConstants.SWITCH_PREFERRED_NAME_TOO_SHORT ) ); 127 } 128 129 final Set newTriggers = new HashSet(); 130 newTriggers.add( enabledPrefix + preferredName ); 131 newTriggers.add( disabledPrefix + preferredName ); 132 m_triggers = Collections.unmodifiableSet( newTriggers ); 133 134 if( aliases == null ) 135 { 136 m_aliases = Collections.EMPTY_SET; 137 } 138 else 139 { 140 m_aliases = Collections.unmodifiableSet( new HashSet( aliases ) ); 141 142 for( final Iterator i = aliases.iterator(); i.hasNext();) 143 { 144 final String alias = (String) i.next(); 145 newTriggers.add( enabledPrefix + alias ); 146 newTriggers.add( disabledPrefix + alias ); 147 } 148 } 149 150 final Set newPrefixes = new HashSet( super.getPrefixes() ); 151 newPrefixes.add( enabledPrefix ); 152 newPrefixes.add( disabledPrefix ); 153 m_prefixes = Collections.unmodifiableSet( newPrefixes ); 154 m_defaultSwitch = switchDefault; 155 checkPrefixes( newPrefixes ); 156 } 157 158 /** 159 * Processes the parent part of the Option. The combination of parent, 160 * argument and children is handled by the process method. 161 * @see net.dpml.cli.Option#process(WriteableCommandLine, ListIterator) 162 * 163 * @param commandLine the CommandLine to write results to 164 * @param arguments a ListIterator over argument strings positioned at the next 165 * argument to process 166 * @throws OptionException if an error occurs while processing 167 */ 168 public void processParent( 169 final WriteableCommandLine commandLine, final ListIterator arguments ) 170 throws OptionException 171 { 172 final String arg = (String) arguments.next(); 173 174 if( canProcess( commandLine, arg ) ) 175 { 176 if( arg.startsWith( m_enabledPrefix ) ) 177 { 178 commandLine.addSwitch( this, true ); 179 arguments.set( m_enabledPrefix + m_preferredName ); 180 } 181 if( arg.startsWith( m_disabledPrefix ) ) 182 { 183 commandLine.addSwitch( this, false ); 184 arguments.set( m_disabledPrefix + m_preferredName ); 185 } 186 } 187 else 188 { 189 throw new OptionException( 190 this, 191 ResourceConstants.UNEXPECTED_TOKEN, 192 arg ); 193 } 194 } 195 196 /** 197 * Identifies the argument prefixes that should trigger this option. This 198 * is used to decide which of many Options should be tried when processing 199 * a given argument string. 200 * 201 * The returned Set must not be null. 202 * 203 * @return The set of triggers for this Option 204 */ 205 public Set getTriggers() 206 { 207 return m_triggers; 208 } 209 210 /** 211 * Identifies the argument prefixes that should be considered options. This 212 * is used to identify whether a given string looks like an option or an 213 * argument value. Typically an option would return the set [--,-] while 214 * switches might offer [-,+]. 215 * 216 * The returned Set must not be null. 217 * 218 * @return The set of prefixes for this Option 219 */ 220 public Set getPrefixes() 221 { 222 return m_prefixes; 223 } 224 225 /** 226 * Checks that the supplied CommandLine is valid with respect to this 227 * option. 228 * 229 * @param commandLine the CommandLine to check. 230 * @throws OptionException if the CommandLine is not valid. 231 */ 232 public void validate( WriteableCommandLine commandLine ) 233 throws OptionException 234 { 235 if( isRequired() && !commandLine.hasOption( this ) ) 236 { 237 throw new OptionException( 238 this, 239 ResourceConstants.OPTION_MISSING_REQUIRED, 240 getPreferredName() ); 241 } 242 super.validate( commandLine ); 243 } 244 245 /** 246 * Appends usage information to the specified StringBuffer 247 * 248 * @param buffer the buffer to append to 249 * @param helpSettings a set of display settings @see DisplaySetting 250 * @param comp a comparator used to sort the Options 251 */ 252 public void appendUsage( 253 final StringBuffer buffer, final Set helpSettings, final Comparator comp ) 254 { 255 // do we display optionality 256 final boolean optional = 257 !isRequired() 258 && helpSettings.contains( DisplaySetting.DISPLAY_OPTIONAL ); 259 260 final boolean displayAliases = 261 helpSettings.contains( DisplaySetting.DISPLAY_ALIASES ); 262 final boolean disabled = 263 helpSettings.contains( DisplaySetting.DISPLAY_SWITCH_DISABLED ); 264 final boolean enabled = 265 !disabled || helpSettings.contains( DisplaySetting.DISPLAY_SWITCH_ENABLED ); 266 final boolean both = disabled && enabled; 267 268 if( optional ) 269 { 270 buffer.append( '[' ); 271 } 272 273 if( enabled ) 274 { 275 buffer.append( m_enabledPrefix ).append( m_preferredName ); 276 } 277 278 if( both ) 279 { 280 buffer.append( '|' ); 281 } 282 283 if( disabled ) 284 { 285 buffer.append( m_disabledPrefix ).append( m_preferredName ); 286 } 287 288 if( displayAliases && !m_aliases.isEmpty() ) 289 { 290 buffer.append( " (" ); 291 292 final List list = new ArrayList( m_aliases ); 293 Collections.sort( list ); 294 for( final Iterator i = list.iterator(); i.hasNext();) 295 { 296 final String alias = (String) i.next(); 297 298 if( enabled ) 299 { 300 buffer.append( m_enabledPrefix ).append( alias ); 301 } 302 303 if( both ) 304 { 305 buffer.append( '|' ); 306 } 307 308 if( disabled ) 309 { 310 buffer.append( m_disabledPrefix ).append( alias ); 311 } 312 313 if( i.hasNext() ) 314 { 315 buffer.append( ',' ); 316 } 317 } 318 319 buffer.append( ')' ); 320 } 321 322 super.appendUsage( buffer, helpSettings, comp ); 323 324 if( optional ) 325 { 326 buffer.append( ']' ); 327 } 328 } 329 330 /** 331 * The preferred name of an option is used for generating help and usage 332 * information. 333 * 334 * @return The preferred name of the option 335 */ 336 public String getPreferredName() 337 { 338 return m_enabledPrefix + m_preferredName; 339 } 340 341 /** 342 * Adds defaults to a CommandLine. 343 * 344 * Any defaults for this option are applied as well as the defaults for 345 * any contained options 346 * 347 * @param commandLine the CommandLine object to store defaults in 348 */ 349 public void defaults( final WriteableCommandLine commandLine ) 350 { 351 commandLine.setDefaultSwitch( this, m_defaultSwitch ); 352 } 353 }